#include "StdAfx.h"
#include "HistFeature.h"
#include "cv.h"
#include "highgui.h"
#include "gdiplus.h"
using namespace Gdiplus;

#pragma comment(lib, "gdiplus")

#define HIST_BINCOUNT	113
#define HIST_HUE		12
#define HIST_SATURATION 3
#define HIST_VALUE		3	
#define HIST_COLOR		108
#define HIST_GRAY		5

#if(HIST_BINCOUNT != HIST_COLOR + HIST_GRAY)
#error "Invalid histogram bin sizes"
#endif

CHistFeature::CHistFeature(void)
: m_iHistSize(256)
{
}

CHistFeature::~CHistFeature(void)
{
}

void CHistFeature::RGB2HSV(byte R, byte G, byte B, float& H, float& S, float& V)
{
	float cmax, cmin, delta;

	cmin = __min(__min(R, G), B);
	cmax = __max(__max(R, G), B);

	V = cmax / 255.0f;
	S = (cmax == 0 ? 0 : 1.0f - (cmin / cmax));
	if (S == 0) {
		H = 0.0f;
	}
	else {
		delta = cmax-cmin;
		if (R == cmax)
			H = 6.0f + (G-B)/delta;
		if (G == cmax)
			H = 2.0f + (B-R)/delta;
		if (B == cmax)
			H = 4.0f + (R-G)/delta;
		H *= 60;
		while(H < 0.0f)
			H += 360.0f;
		while(H >= 360.0f)
			H -= 360.0f;
	}
}

BOOL CHistFeature::Calc(Bitmap* bmp, OUT CArray<double>& arrFeatures)
{
	//ASSERT(bmp->GetPixelFormat() == PixelFormat32bppARGB);
	BitmapData bmpData;
	if(bmp->LockBits(&Rect(0, 0, bmp->GetWidth(), bmp->GetHeight()), ImageLockModeRead, PixelFormat32bppARGB, &bmpData) != Ok)
	{
		ASSERT(FALSE);
		return FALSE;
	}
	
	byte bBlue, bGreen, bRed;
	float H, S, V;
	const int iHStep = (int)(255 / HIST_HUE);			// we have 12 bins of Hue
	const int iSStep = 255 / HIST_SATURATION;			// we have 3 bins of Saturation and Value
	const int iVStep = 255 / HIST_VALUE;
	const int iGrayStep = 255 / HIST_GRAY;
	int idxH, idxS, idxV, idxGray;

	arrFeatures.RemoveAll();
	arrFeatures.SetSize(HIST_BINCOUNT);
	for(int i = 0; i < HIST_BINCOUNT; arrFeatures[i++] = 0);

	byte* arrData = (byte*)bmpData.Scan0;

	for(int i = bmp->GetWidth() - 1; i >= 0; --i)
	{
		for(int j = bmp->GetHeight() - 1; j >= 0; --j)
		{
			bBlue = *(arrData + (j * bmpData.Stride)+(i * 4));
			bGreen = *(arrData + (j * bmpData.Stride)+(i * 4) + 1);
			bRed = *(arrData + (j * bmpData.Stride)+(i * 4) + 2);
			RGB2HSV(bRed, bGreen, bBlue, H, S, V);

			idxH = min((int)floor(H / iHStep), HIST_HUE - 1) + 1;
			idxS = min((int)floor(S / iSStep), HIST_SATURATION - 1) + 1;
			idxV = min((int)floor(V / iVStep), HIST_VALUE - 1) + 1;
			idxGray = min((int)floor(V / iGrayStep), HIST_GRAY - 1);
			arrFeatures[idxH*idxS*idxV - 1]++;
			arrFeatures[HIST_COLOR + idxGray]++;
		}
	}
	bmp->UnlockBits(&bmpData);
	return TRUE;
}

BOOL CHistFeature::Calc(CString& sPath, OUT CArray<double>& arrFeatures)
{
	IplImage* img = NULL;
	char arrFileName[MAX_PATH];
	CFeatureExtract::CString2Char(sPath, arrFileName);
	img = cvvLoadImage(arrFileName);

	if(img)
	{
		IplImage* imgHSV, *imgH, *imgS, *imgV;
		CvSize szImg;

		imgHSV = cvCreateImage(cvGetSize(img), 8, 3);
		imgH = cvCreateImage(cvGetSize(img), 8, 1);
		imgS = cvCreateImage(cvGetSize(img), 8, 1);
		imgV = cvCreateImage(cvGetSize(img), 8, 1);

		cvCvtColor(img, imgHSV, CV_RGB2HSV);
		cvCvtPixToPlane(imgHSV, imgH, imgS, imgV, NULL);
		cvReleaseImage(&imgHSV);
		cvReleaseImage(&img);

		szImg = cvGetSize(imgH);

		arrFeatures.RemoveAll();
		arrFeatures.SetSize(HIST_BINCOUNT);
		for(int i = 0; i < HIST_BINCOUNT; arrFeatures[i++] = 0);
		
#define GETPIXEL(I, x, y) ((uchar*)((I)->imageData + (I)->widthStep*(y)))[(x)]

		const int iHStep = (int)(255 / HIST_HUE);			// we have 12 bins of Hue
		const int iSStep = 255 / HIST_SATURATION;			// we have 3 bins of Saturation and Value
		const int iVStep = 255 / HIST_VALUE;
		const int iGrayStep = 255 / HIST_GRAY;
		int idxH, idxS, idxV, idxGray;

		for(int i = szImg.width - 1; i >= 0; --i)
		{
			for(int j = szImg.height - 1; j >= 0; --j)
			{
				idxH = min(GETPIXEL(imgH, i, j) / iHStep, HIST_HUE - 1) + 1;
				idxS = min(GETPIXEL(imgS, i, j) / iSStep, HIST_SATURATION - 1) + 1;
				idxV = min(GETPIXEL(imgV, i, j) / iVStep, HIST_VALUE - 1) + 1;
				idxGray = min(GETPIXEL(imgV, i, j) / iGrayStep, HIST_GRAY - 1);
				arrFeatures[idxH*idxS*idxV - 1]++;
				arrFeatures[HIST_COLOR + idxGray]++;
			}
		}
#undef GETPIXEL

		cvReleaseImage(&imgH);
		cvReleaseImage(&imgS);
		cvReleaseImage(&imgV);

		return TRUE;
	}
	return FALSE;

	//IplImage* img = NULL;
	//char arrFileName[MAX_PATH];
	//CFeatureExtract::CString2Char(sPath, arrFileName);
	//img = cvvLoadImage(arrFileName);

	//if(img)
	//{
	//	IplImage* imgHSV, *imgH, *imgS, *imgV;
	//	CvHistogram* hist;
	//	imgHSV = cvCreateImage(cvGetSize(img), 8, 3);
	//	imgH = cvCreateImage(cvGetSize(img), 8, 1);
	//	imgS = cvCreateImage(cvGetSize(img), 8, 1);
	//	imgV = cvCreateImage(cvGetSize(img), 8, 1);

	//	cvCvtColor(img, imgHSV, CV_RGB2HSV);
	//	cvCvtPixToPlane(imgHSV, imgH, imgS, imgV, NULL);
	//	hist = cvCreateHist(1, &m_iHistSize, CV_HIST_ARRAY);
	//	
	//	cvCalcHist(&imgV, hist, 0, NULL);
	//	arrFeatures.RemoveAll();
	//	for(int i = 0; i < m_iHistSize; ++i)
	//	{
	//		arrFeatures.Add(*cvGetHistValue_1D(hist, i));
	//	}
	//	cvReleaseImage(&imgHSV);
	//	cvReleaseImage(&imgH);
	//	cvReleaseImage(&imgS);
	//	cvReleaseImage(&imgV);
	//	cvReleaseImage(&img);
	//	cvReleaseHist(&hist);

	//	return TRUE;
	//}
	//return FALSE;
}
